import re
import json
import sys

import cpca

from Train import AddressMatchingModelTrain
import pandas as pd
import time
import os
current_path = sys.path[0]
DATA_FILE_PATH = os.path.join(current_path, 'file\\')
def timer(func):
    def call_func(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        total_time = end_time - start_time
        print(f"{func.__name__}用时{int(total_time // 60)}分{total_time % 60:.3f}秒")
        return result

    return call_func


# 关键字匹配算法模型类
# 通过关键字匹配的方式得出正确地址
class AddressMatchingModel:
    def __init__(self):
        self.houseNumberSuffix = set()  # 门牌后缀
        self.buildingNumberSuffix = set()  # 栋楼后缀
        self.roomNumberSuffix = set()  # 室号后缀
        self.JLS = set()  # 所有的街路巷

    def save(self):
        """

        :param fileName:
        :return:
        """
        map = {}

        map['JLS'] = list(self.JLS)
        map['houseNumberSuffix'] = list(self.houseNumberSuffix)
        map['buildingNumberSuffix'] = list(self.buildingNumberSuffix)
        map['roomNumberSuffix'] = list(self.roomNumberSuffix)
        # j = json.dumps(map,ensure_ascii=False)
        with open(DATA_FILE_PATH +'标准模型.json' , 'w', encoding='utf8') as f:
            f.write(json.dumps(map, indent=4, ensure_ascii=False))

    def load(self):
        """
        :param fileName:
        :return:
        """
        with open(DATA_FILE_PATH+'标准模型.json', 'r',encoding='utf8') as load_f:
            load_dict = json.load(load_f)
        self.JLS = load_dict['JLS']
        self.roomNumberSuffix = load_dict['roomNumberSuffix']
        self.houseNumberSuffix = load_dict['houseNumberSuffix']
        self.buildingNumberSuffix = load_dict['buildingNumberSuffix']


    # 模型预测得到一个地址类Address
    def predict(self, address):
        ret = Address()
        # 提取行政区划
        temp = self.get_XZQH(address)
        ret.XZQH = temp[0]
        for i in range(1, 4):
            address = address.replace(temp[i], "")  # 将识别出来的结果从地址里去掉
            address = address.replace(temp[i][0:-1], "")

        # 提取街路巷
        ret.JLX = self.get_JLX(address)
        address = address.replace(ret.JLX, "")

        # 提取38号  7幢  三单元  111室等信息
        pattern1 = "[零一二三四五六七八九0-9]+[^零一二三四五六七八九0-9]+"  # 提取 38号  7幢  三单元  111室
        for item in re.findall(pattern1, address):
            typ = self.parseNumberType(item)
            if typ == 1:
                if ret.houseNumber == "":
                    ret.houseNumber = item
                else:
                    ret.buildingNumber = item
            elif typ == 2:
                ret.buildingNumber = item
            elif typ == 3:
                ret.roomNumber = item
            elif typ == 4:
                ret.unitNumber = item

        # 到最后街路巷也没有数据则将address去掉这些号作为街道巷
        if ret.JLX == "":
            address = address.replace(ret.houseNumber, "")
            address = address.replace(ret.buildingNumber, "")
            address = address.replace(ret.roomNumber, "")
            address = address.replace(ret.unitNumber, "")
            ret.JLX = address

        return ret

    # 获取街路巷
    def get_JLX(self, address):
        ret = ""
        ret_len = 0
        for item in self.JLS:
            if item in address and len(item) > ret_len:  # 如果当前匹配到的信息比之前的长，则我们认为它更加匹配
                ret = item
                ret_len = len(item)
        return ret

    # 获取行政区划
    def get_XZQH(self, address):
        cur = cpca.transform([address])
        sumAddr = str(cur.values[0][0]) + str(cur.values[0][1]) + str(cur.values[0][2])

        return [sumAddr, str(cur.values[0][0]), str(cur.values[0][1]), str(cur.values[0][2])]

    # demo版的判断类型，给出的门牌还有前缀，栋楼也有前缀，门派和栋楼都可能，可根据先后顺序来给类型，先门牌后栋楼
    def parseNumberType(self, number):
        # 解析门牌
        for suffix in self.houseNumberSuffix:
            if suffix in number:
                return 1

        # 解析栋楼
        for suffix in self.buildingNumberSuffix:
            if suffix in number:
                return 2

        # 解析室号
        for suffix in self.roomNumberSuffix:
            if suffix in number:
                return 3

        # 单元
        if "单元" in number:
            return 4


class Address:
    def __init__(self):
        self.XZQH = ""  # 行政区划
        self.JLX = ""  # 街道巷
        self.houseNumber = ""  # 门牌
        self.buildingNumber = ""  # 栋楼
        self.roomNumber = ""  # 室号
        self.unitNumber = ""  # 单元号

    def __str__(self):
        return "行政区划:" + self.XZQH + "\t街路巷:" + self.JLX + "\t门牌:" + self.houseNumber + "\t栋楼:" + self.buildingNumber + "\t单元号:" + self.unitNumber + "\t室号:" + self.roomNumber


@timer
def test():
    model = AddressMatchingModel()

    # 训练模型
    # train = AddressMatchingModelTrain("标准地址结构化数据样例.csv")
    # train.train(model)


    model.load()
    test_address_data = pd.read_csv("./file/非标准地址数据样例.csv")["非标准地址"].values
    # 预测非标准地址
    for address in test_address_data:
        ret = model.predict(address)
        # print("原始地址：" + address, end="\t")
        print(ret)
    # print("街路巷条数：", len(model.JLS))


# 解题思路：
# （原地址江苏南京市建邺区友谊街38号7幢三单元611室）
# 1.通过专家系统提取行政区划，从地址去掉行政区划  (处理后得到 江苏南京市建邺区， 剩下 友谊街38号7幢三单元611室)
# 2.通过提供的街路巷数据匹配街路巷 从地址去掉街路巷 (处理后得到 友谊街， 剩下 38号7幢三单元611室)
# 3.使用正则将数字加文字等信息分开，挨个通过后缀匹配的方式判断类型(处理后得到38号  7幢  三单元  611室)

# 存在的问题：
# 1.门牌后缀与栋楼后缀有一样的比如 "号"
# 解决办法：先门牌，后栋楼
# 其它办法：是否考虑可以加上字典提供的前缀
# 2. 提供的街路巷数据并不能完全匹配所有的测试数据
# 解决办法：假使解题思路中3个步骤后街路巷依然为空，则可以通过去掉号码等信息后的数据作为街路巷
# 3.号牌"xx-xx" (江苏南京市秦淮区白衣庵17号-2) (江苏南京市建邺区兆园115号-1号) 号牌通过"-"或"－"连接起来的，取第一个xx作为门牌号，第二个数据要舍去不能作为栋楼了
# 解决办法 特殊处理
# 4 效率问题
# 解决方法:
# （1）街路巷的匹配问题, 建立索引的方式，可以通过字典将两万条数据拆成一颗树
# 胜利村路
# 胜利一村
# 胜利二村
# 胜利三村
# dic = {"胜": {
#             "利": {
#                 "一": {
#                     "村": None
#                 },
#                 "二": {
#                     "村": None
#                 },
#                 "三": {
#                     "村": None
#                 }
#             }
#         }
#     }
# 递归一个字一个字的匹配知道为None
# （2）可以用问题2去掉号牌就是街路巷的方式代换，但是存在的问题是少数数据形如“棉鞋营六十号三弄”这是街路巷号牌识别会将六十号给提取出来

if __name__ == "__main__":
    test()
